package com.xbongbong.process;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.OptionalRangeEntity;
import com.xbongbong.paas.domain.entity.ext.PaasProcessTemplateEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasProcessTemplateNodeEntityExt;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.enums.NodeTypeEnum;
import com.xbongbong.paas.enums.OptionalRangeEnum;
import com.xbongbong.paas.enums.ProcessEmptyApproverTypeEnum;
import com.xbongbong.paas.enums.TaskTypeEnum;
import com.xbongbong.paas.help.CompareValueByConditionHelp;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.model.PaasProcessTemplateModel;
import com.xbongbong.paas.model.PaasProcessTemplateNodeModel;
import com.xbongbong.paas.pojo.dto.ProcessConditionValidDTO;
import com.xbongbong.paas.pojo.dto.ProcessNodeTaskAddDTO;
import com.xbongbong.paas.pojo.dto.StartProcessDTO;
import com.xbongbong.paas.pojo.vo.StartProcessVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.DepartmentVO;
import com.xbongbong.paas.toolbox.wrap.RoleSimpleVO;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.process.operate.PaasProcessOperateService;
import com.xbongbong.process.task.NodeTaskListStartProcessServiceImpl;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.model.DepartmentModel;
import com.xbongbong.sys.model.UserModel;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Description: 开启流程help
 * @author 李杰
 * @date 2019/4/19 15:00
 * @version v1.0
 * @since v1.0
 */
@Component
public class PostPositionProcessHelp {

    private static  final Logger LOG = LoggerFactory.getLogger(PostPositionProcessHelp.class);

    @Resource
    private PaasProcessOperateService paasProcessOperateService;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private PaasProcessTemplateModel paasProcessTemplateModel;
    @Resource
    private PaasProcessTemplateNodeModel paasProcessTemplateNodeModel;
    @Resource
    private NodeTaskListStartProcessServiceImpl nodeTaskListStartProcessService;
    @Resource
    private UserModel userModel;
    @Resource
    private DepartmentModel departmentModel;
    @Resource
    private CompareValueByConditionHelp compareValueByConditionHelp;

    public StartProcessVO startProcess(StartProcessDTO startProcessDTO) throws XbbException {
        StartProcessVO startProcessVO = new StartProcessVO();
        try {
            // 查询审批模板
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("del", 0);
            Integer businessType = startProcessDTO.getBusinessType();
            // 只有经销商的退货退款，回款单，发票需要后置进审批
            if (Objects.equals(startProcessDTO.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && XbbRefTypeEnum.postPositionBusinessType(businessType, startProcessDTO.getDistributorMark())) {
                params.put("formId", startProcessDTO.getFormId());
            } else {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
            }
            params.put("saasMark", startProcessDTO.getSaasMark());
            params.put("corpid", startProcessDTO.getCorpid());
            params.put("enable", 1);
            List<PaasProcessTemplateEntityExt> processList = paasProcessTemplateModel.list(params);
            if (processList.size() > 1) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PROCESS_OPEN_ERROR);
            }
            // 防止前端返回空数据进审批
            if ((Objects.isNull(startProcessDTO.getData()) || startProcessDTO.getData().isEmpty())) {
                startProcessVO.setNoApprovalRequired(0);
                return startProcessVO;
            }

            if (!processList.isEmpty()) {
                PaasProcessTemplateEntityExt paasProcessTemplateEntityExt = processList.get(0);
                if (startProcessDTO.getFormDataId() != null && !Objects.equals(startProcessDTO.getFormDataId(), 0L)) {
                    params.put("formId", startProcessDTO.getFormId());
                    params.put("businessType", businessType);
                    params.put("flowStatusIn", Arrays.asList(FlowStatusEnum.IN_APPROVAL.getType(), FlowStatusEnum.APPROVAL_PENDING.getType()));
                    params.put("formDataId", startProcessDTO.getFormDataId());
                    Integer count = paasProcessDataModel.getEntitysCount(params);
                    if (count > 0) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.DATA_HAVE_IN_PROCESS);
                    }
                }

                // 无需审批角色判断
                Set<String> roleList = new HashSet<>();
                Set<String> departmentList = new HashSet<>();
                Set<String> userList = new HashSet<>();
                List<OptionalRangeEntity> rolesWithoutApproveList = paasProcessTemplateEntityExt.getRolesWithoutApproveList();
                for(OptionalRangeEntity optionalRangeEntity: rolesWithoutApproveList) {
                    if(Objects.equals(optionalRangeEntity.getProperty(), OptionalRangeEnum.DEPT.getValue())){
                        departmentList.add(optionalRangeEntity.getId());
                    } else if (Objects.equals(optionalRangeEntity.getProperty(), OptionalRangeEnum.ROLE.getValue())){
                        roleList.add(optionalRangeEntity.getId());
                    } else if (optionalRangeEntity.getProperty().equals(OptionalRangeEnum.USER.getValue())){
                        userList.add(optionalRangeEntity.getId());
                    }
                }
                // fix 24764
                String creatorId = startProcessDTO.getCreatorId();
                UserVO userVO = userModel.getUserVO(startProcessDTO.getCorpid(), creatorId, true, false);
                Set<RoleSimpleVO> roleSimpleVOSet = userVO.getRoleSet();
                Set<DepartmentVO> departmentVOSet = userVO.getDepSet();
                if (!userList.isEmpty()) {
                    if (userList.contains(creatorId)) {
                        return startProcessVO;
                    }
                }
                if (!roleList.isEmpty()) {
                    for (RoleSimpleVO roleSimpleVO : roleSimpleVOSet) {
                        if (roleList.contains(roleSimpleVO.getId().toString())) {
                            return startProcessVO;
                        }
                    }
                }
                if (!departmentList.isEmpty()) {
                    for (DepartmentVO departmentVO : departmentVOSet) {
                        if (departmentList.contains(departmentVO.getId().toString())) {
                            return startProcessVO;
                        }
                    }
                }
                if(Objects.equals(paasProcessTemplateEntityExt.getEnable(), 1)){
                    startProcessDTO.setData(startProcessDTO.getData());
                    startProcessDTO.setFormDataId(startProcessDTO.getFormDataId());
                    startProcessDTO.setFlowStatus(FlowStatusEnum.APPROVAL_PENDING.getType());
                    startProcessDTO.setTemplateId(paasProcessTemplateEntityExt.getId());
                    startProcessDTO.setMenuId(paasProcessTemplateEntityExt.getMenuId());
                    startProcessDTO.setPostPosition(1);
                    startProcessVO = paasProcessOperateService.dingStartProcess(startProcessDTO);
                    if(startProcessVO == null) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.START_PROCESS_ERROR);
                    }
                }
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("formData.startProcess流程任务生成失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return startProcessVO;
    }

    public boolean isAutoReject (StartProcessDTO startProcessDTO) throws XbbException{
        // 查询审批模板
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("del", 0);
        Integer businessType = startProcessDTO.getBusinessType();
        // 只有经销商的退货退款，回款单，发票需要后置进审批
        if (Objects.equals(startProcessDTO.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && XbbRefTypeEnum.postPositionBusinessType(businessType, startProcessDTO.getDistributorMark())) {
            params.put("formId", startProcessDTO.getFormId());
        } else {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        params.put("saasMark", startProcessDTO.getSaasMark());
        params.put("corpid", startProcessDTO.getCorpid());
        params.put("enable", 1);
        List<PaasProcessTemplateEntityExt> processList = paasProcessTemplateModel.list(params);
        if (processList.size() > 1) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.PROCESS_OPEN_ERROR);
        }
        if (!processList.isEmpty()) {
            PaasProcessTemplateEntityExt paasProcessTemplateEntityExt = processList.get(0);
            // 无需审批角色判断
            Set<String> roleList = new HashSet<>();
            Set<String> departmentList = new HashSet<>();
            Set<String> userList = new HashSet<>();
            List<OptionalRangeEntity> rolesWithoutApproveList = paasProcessTemplateEntityExt.getRolesWithoutApproveList();
            for(OptionalRangeEntity optionalRangeEntity: rolesWithoutApproveList) {
                if(Objects.equals(optionalRangeEntity.getProperty(), OptionalRangeEnum.DEPT.getValue())){
                    departmentList.add(optionalRangeEntity.getId());
                } else if (Objects.equals(optionalRangeEntity.getProperty(), OptionalRangeEnum.ROLE.getValue())){
                    roleList.add(optionalRangeEntity.getId());
                } else if (optionalRangeEntity.getProperty().equals(OptionalRangeEnum.USER.getValue())){
                    userList.add(optionalRangeEntity.getId());
                }
            }
            // fix 24764
            String creatorId = startProcessDTO.getCreatorId();
            UserVO userVO = userModel.getUserVO(startProcessDTO.getCorpid(), creatorId, true, false);
            Set<RoleSimpleVO> roleSimpleVOSet = userVO.getRoleSet();
            Set<DepartmentVO> departmentVOSet = userVO.getDepSet();
            if (!userList.isEmpty()) {
                if (userList.contains(creatorId)) {
                    return false;
                }
            }
            if (!roleList.isEmpty()) {
                for (RoleSimpleVO roleSimpleVO : roleSimpleVOSet) {
                    if (roleList.contains(roleSimpleVO.getId().toString())) {
                        return false;
                    }
                }
            }
            if (!departmentList.isEmpty()) {
                for (DepartmentVO departmentVO : departmentVOSet) {
                    if (departmentList.contains(departmentVO.getId().toString())) {
                        return false;
                    }
                }
            }
            boolean isAutoReject = Objects.equals(paasProcessTemplateEntityExt.getEmptyApproverType(), ProcessEmptyApproverTypeEnum.AUTO_REJECT.getValue());
            if (isAutoReject) {
                params.clear();
                params.put("del", DelEnum.NORMAL.getDel());
                params.put("corpid", startProcessDTO.getCorpid());
                params.put("templateId", startProcessDTO.getTemplateId());
                params.put("type", NodeTypeEnum.START_PROCESS_NODE.getNodeType());
                // 查找开始节点
                List<PaasProcessTemplateNodeEntityExt> startNodeList = paasProcessTemplateNodeModel.list(params);
                if (startNodeList.isEmpty()) {
                    return false;
                }
                PaasProcessTemplateNodeEntityExt templateNodeEntityExt = startNodeList.get(0);
                return isAutoReject(templateNodeEntityExt, startProcessDTO);
            }
        }
        return false;
    }


    private boolean isAutoReject(PaasProcessTemplateNodeEntityExt templateNodeEntityExt, StartProcessDTO startProcessDTO) throws XbbException {
        String lastNodeId = templateNodeEntityExt.getUid();
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", templateNodeEntityExt.getCorpid());
        params.put("prevId", lastNodeId);
        params.put("formId", templateNodeEntityExt.getFormId());
        params.put("orderByStr", " priority " + SortOrder.ASC);
        params.put("del", 0);
        params.put("templateId", templateNodeEntityExt.getTemplateId());
        params.put("menuId", templateNodeEntityExt.getMenuId());
        List<PaasProcessTemplateNodeEntityExt> nextNodeList = paasProcessTemplateNodeModel.list(params);
        if (nextNodeList.isEmpty()) {
            String wrapperId = templateNodeEntityExt.getWrapperId();
            // wrapper外面包一层wrapper的情况
            return isAutoReject(startProcessDTO, params, wrapperId);
        } else {
            // 当前为wrapper节点时，子节点应该去判断条件节点
            if (Objects.equals(templateNodeEntityExt.getType(), NodeTypeEnum.CONDITION_WRAPPER_PROCESS_NODE.getNodeType())) {
                List<PaasProcessTemplateNodeEntityExt> nextConditionNodeList = new ArrayList<>();
                nextNodeList.forEach(item -> {
                    if (Objects.equals(item.getType(), NodeTypeEnum.CONDITION_PROCESS_NODE.getNodeType())) {
                        nextConditionNodeList.add(item);
                    }
                });
                // 判断条件节点任务是否符合 返回符合条件的条件节点
                PaasProcessTemplateNodeEntityExt nextConditionNode = conditionIsMet(nextConditionNodeList, startProcessDTO);
                if (nextConditionNode == null) {
                    return false;
                } else {
                    // 继续向下推进任务
                    return isAutoReject(nextConditionNode, startProcessDTO);
                }
            } else {
                // 其他类型节点时，只有一个子节点
                if (nextNodeList.size() > 1) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                }
                PaasProcessTemplateNodeEntityExt nextNode = nextNodeList.get(0);
                if (Objects.equals(nextNode.getType(), NodeTypeEnum.CONDITION_WRAPPER_PROCESS_NODE.getNodeType())) {
                    return isAutoReject(nextNode, startProcessDTO);
                } else {
                    if (Objects.equals(nextNode.getType(), NodeTypeEnum.CC_PROCESS_NODE.getNodeType())) {
                        return isAutoReject(nextNode, startProcessDTO);
                    } else {
                        List<OptionalRangeEntity> userList = nextNode.getMainUserList();
                        // 生成负责人任务
                        ProcessConditionValidDTO processConditionValidDTO = new ProcessConditionValidDTO();
                        BeanUtil.copyProperties(startProcessDTO, processConditionValidDTO);
                        List<ProcessNodeTaskAddDTO> userNodeTaskList = nodeTaskListStartProcessService.getNodeTaskList(userList, processConditionValidDTO, nextNode, TaskTypeEnum.UNTREATED.getType());
                        return userNodeTaskList.isEmpty();
                    }
                }
            }
        }
    }

    private boolean isAutoReject(StartProcessDTO startProcessDTO, Map<String, Object> params, String wrapperId) throws XbbException {
        if (StringUtil.isEmpty(wrapperId)) {
            return false;
        } else {
            // 如果有wrapperId说明分支块走完，查找下一个非分支节点的子节点
            params.put("prevId", wrapperId);
            params.put("typeNotIn", Collections.singletonList(NodeTypeEnum.CONDITION_PROCESS_NODE.getNodeType()));
            List<PaasProcessTemplateNodeEntityExt> nodeList = paasProcessTemplateNodeModel.list(params);
            if (nodeList.isEmpty()) {
                params.remove("prevId");
                params.remove("typeNotIn");
                params.put("uid", wrapperId);
                List<PaasProcessTemplateNodeEntityExt> wapperNodeList = paasProcessTemplateNodeModel.list(params);
                params.remove("uid");
                if (!wapperNodeList.isEmpty()) {
                    wrapperId = wapperNodeList.get(0).getWrapperId();
                    if (!StringUtil.isEmpty(wrapperId)) {
                        return isAutoReject(startProcessDTO, params, wrapperId);
                    } else {
                        return false;
                    }
                } else {
                    return false;
                }
            } else {
                PaasProcessTemplateNodeEntityExt nextNode = nodeList.get(0);
                if (Objects.equals(nextNode.getType(), NodeTypeEnum.CONDITION_WRAPPER_PROCESS_NODE.getNodeType())
                        || Objects.equals(nextNode.getType(), NodeTypeEnum.CC_PROCESS_NODE.getNodeType())) {
                    return isAutoReject(nextNode, startProcessDTO);
                } else {
                    List<OptionalRangeEntity> userList = nextNode.getMainUserList();
                    // 生成负责人任务
                    ProcessConditionValidDTO processConditionValidDTO = new ProcessConditionValidDTO();
                    BeanUtil.copyProperties(startProcessDTO, processConditionValidDTO);
                    List<ProcessNodeTaskAddDTO> userNodeTaskList = nodeTaskListStartProcessService.getNodeTaskList(userList, processConditionValidDTO, nextNode, TaskTypeEnum.UNTREATED.getType());
                    return userNodeTaskList.isEmpty();
                }
            }
        }
    }

    private PaasProcessTemplateNodeEntityExt conditionIsMet(List<PaasProcessTemplateNodeEntityExt> nextConditionNode, StartProcessDTO startProcessDTO) throws XbbException {
        boolean result = false;
        boolean elseResult = false;
        PaasProcessTemplateNodeEntityExt elseProcessCondition = new PaasProcessTemplateNodeEntityExt();
        PaasProcessTemplateNodeEntityExt processCondition = new PaasProcessTemplateNodeEntityExt();
        UserEntity userEntity = userModel.getByKey(startProcessDTO.getCreatorId(), startProcessDTO.getCorpid());
        for (PaasProcessTemplateNodeEntityExt item : nextConditionNode) {
            processCondition = item;
            // else 条件
            if (Objects.equals(item.getConditionType(),2)) {
                elseResult = true;
                elseProcessCondition = item;
                continue;
            }
            // 流转条件为空，且不为结束节点
            boolean conditionIsEmpty = item.getConditions() == null || item.getConditions().isEmpty();
            if (conditionIsEmpty) {
                // 当条件为null时，直接生成下一条任务
                result = true;
                break;
            } else {
                List<ConditionsEntityExt> itemConditions = item.getConditions();
                for (ConditionsEntityExt condition : itemConditions) {
                    String conditionAttr = condition.getAttr();
                    Object value;
                    if (Objects.equals(conditionAttr, FieldTypeEnum.SERIALNO.getAlias())) {
                        value = startProcessDTO.getSerialNo();
                    } else if (Objects.equals(conditionAttr, FieldTypeEnum.DEPARTMENTID.getAlias())) {
                        value = startProcessDTO.getDepartmentId();
                    } else if (Objects.equals(conditionAttr, FieldTypeEnum.UPDATETIME.getAlias())) {
                        value = DateTimeUtil.getInt();
                    } else if (Objects.equals(conditionAttr, FieldTypeEnum.ADDTIME.getAlias())) {
                        value = DateTimeUtil.getInt();
                    } else if (Objects.equals(conditionAttr, FieldTypeEnum.CREATORID.getAlias())) {
                        value = startProcessDTO.getCreatorId();
                    } else {
                        value = startProcessDTO.getData().get(condition.getAttr());
                    }
                    if (Objects.equals(conditionAttr, "creator_dept")) {
                        result = false;
                        if (userEntity != null) {
                            List<Object> conditionList = condition.getValue();
                            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                            StringBuilder depIdInRegexp = new StringBuilder();
                            for (int i = 0; i < conditionList.size(); i++) {
                                String router = Objects.toString(conditionList.get(i), "0");
                                if (StringUtil.isNotEmpty(router)) {
                                    depIdInRegexp.append("(\\|").append(router).append("\\|)");
                                    if (i != conditionList.size() - 1) {
                                        depIdInRegexp.append("|");
                                    }
                                }
                            }
                            param.put("del", 0);
                            param.put("corpid", startProcessDTO.getCorpid());
                            /*
                             * 数据库内dep_id_router字段的数值格式为：|1|2|4|
                             * 如果想获取部门id为1、2的部门的所有子部门，则等于获取dep_id_router所有包含"|1|"或"|2|"的
                             */
                            param.put("routerRegexp", depIdInRegexp.toString());
                            List<Long> perList = departmentModel.getDepIds(param);

                            String dept = userEntity.getDepartment();
                            JSONArray deptArray = JSON.parseArray(dept);
                            for (int i = 0; i < deptArray.size(); i++) {
                                Long router = deptArray.getLong(i);
                                if (perList.contains(router)) {
                                    result = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        try {
                            result = compareValueByConditionHelp.compareValueByCondition(condition, value);
                        } catch (XbbException e) {
                            throw e;
                        } catch (Exception e) {
                            LOG.error("PaasProcessCondition.judgingCondition数据库流转条件判断操作出错", e);
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
                        }
                    }
                    if (!result) {
                        break;
                    }
                }
                if (result) {
                    break;
                }
            }
        }
        if (result) {
            // 满足条件，则生成任务
            return processCondition;
        } else if (elseResult) {
            // 当所有条件不满足，但是有else时，走此流程
            return elseProcessCondition;
        } else {
            // 所有条件不满足，则通过流程
            return null;
        }
    }
}
